import os
from serial import Serial as OriginSerial

import logging

logger = logging.getLogger("ssjhs")

SerialPortHead = "ttyACM"
EndBytes = "ssjhs:/$ \r\nssjhs:/$ ".encode()

Baudrate = 512000


# 截取SerialPort的write和read的异常进行报错
class Serial(OriginSerial):
    def setErrorCallback(self, callback):
        self.callback = callback
        self.is_callback = True

    def setName(self, name):
        self.name = name

    def write(self, data):
        try:
            ret = super().write(data)
        except Exception as e:
            name = self.name if hasattr(self, "name") else self.portstr
            print("\033[1;31m" + f"{name} serial port error:" + str(e) + "\033[0m")
            if hasattr(self, "callback") and self.callback is not None and self.is_callback:
                self.callback(name)
            raise Exception(f"{name} serial port error: {e}")
        return ret

    def read(self, size):
        try:
            ret = super().read(size)
        except Exception as e:
            name = self.name if hasattr(self, "name") else self.portstr
            print("\033[1;31m" + f"{name} serial port error:" + str(e) + "\033[0m")
            if hasattr(self, "callback") and self.callback is not None and self.is_callback:
                self.callback(name)
            raise Exception(f"{name} serial port error: {e}")

        return ret


class SerialPort:
    @staticmethod
    def getSerialPortMap():
        global DeviceMap
        import glob

        deviceMap = {"Camera": None, "MLCU": None, "PRT": None}

        devices = glob.glob("/dev/ttyACM*")
        for dev in devices:
            cmd = f"udevadm info {dev} |grep ID_MODEL"
            """
            return: 'E: ID_MODEL=OCM\nE: ID_MODEL_ENC=OCM\nE: ID_MODEL_ID=00a4\n'
            """
            result = os.popen(cmd)
            x = result.read()
            for name in deviceMap:
                a = x.find(f"ID_MODEL={name}")
                if a >= 0:
                    deviceMap[name] = dev

        deviceMap["MLCU"] = "/dev/ttyAMA0"

        DeviceMap = deviceMap
        return DeviceMap

    @staticmethod
    def getSerialPortMap_lagacy():
        port_MLCU, port_OCM = None, None
        indexes = []
        for root, list, files in os.walk("/dev/"):
            for file in files:
                if SerialPortHead in file:
                    i = file[len(SerialPortHead) :]
                    i = int(i)
                    indexes.append(i)

        for i in range(3):
            for port in indexes:
                sp = SerialPort("/dev/" + SerialPortHead + str(port), timeout=0.1)
                # result = sp.write('')
                # sp.set_timeout(0.1)
                result = sp.read("device")
                result = sp.read("device")
                result = sp.read("device")
                sp.close()
                # print('serial port:',result)
                if result is None:
                    continue
                try:
                    b = result.to_bytes(4, "big", signed=False)
                    s = str(b, encoding="utf-8")
                except:
                    s = ""
                if s == "MLCU":
                    port_MLCU = port
                if s == "OCM\x00":
                    port_OCM = port
                if s == "PRT\x00":
                    port_PRT = port

                # print(f'{SerialPortHead:s}{port:s}  {s:s}')

            if port_MLCU is not None and port_OCM is not None:
                break

        return port_MLCU, port_OCM

    def __init__(self, com, timeout=0.1):
        self.error_callback = None
        self.open(com, timeout)

    def open(self, com, timeout):
        self.com = com
        self.timeout = timeout
        self.is_open = False
        self.port = Serial(self.com, timeout=self.timeout, baudrate=Baudrate)

        data = self.port.read(256)
        self.is_open = True

    def close(self):
        if self.is_open:
            self.is_open = False
            self.port.cancel_read()
            self.port.cancel_write()
            self.port.close()

    def write(self, cmd):
        cmd += "\r\n"
        self.port.write(cmd.encode())
        # recv = self.port.read(len(cmd)).decode()
        recv = self.port.read_until(EndBytes).decode()
        pass

    #####################################################################
    # reigon private
    def writeOnly(self, cmd):
        cmd += "\r\n"
        self.port.write(cmd.encode())

    def writeData(self, data):
        self.port.write(data)

    def readOnly(self):
        recv = self.port.read_until(EndBytes).decode()
        return recv

    def readUntil(self, end_bytes):
        recv = self.port.read_until(end_bytes.encode()).decode()
        return recv

    def readData(self, length):
        recv = self.port.read(length)
        return recv

    def readEnd(self):
        recv = self.port.read_until(EndBytes).decode()
        return recv

    # endreigon private
    #####################################################################
    def read(self, cmd):
        cmd += "\r\n"
        keyword = "Return: "
        self.port.write(cmd.encode())
        # recv = self.port.read(len(cmd)).decode()
        recv = self.port.read_until(EndBytes).decode()
        # self.port.timeout = 2
        # recv = self.port.read(100)
        # self.port.timeout = None
        # print(recv)

        i = recv.find(keyword)
        if i < 0:
            if "device" not in cmd:
                print("recv", recv)
                logger.error("serial port data error!")

                name = self.port.name if hasattr(self.port, "name") else self.port.portstr
                print("\033[1;31m" + f"{name} serial port data error!" + "\033[0m")

            return None

        str = recv[i + len(keyword) : len(recv)]
        array = str.split(",")
        if len(array) == 0:
            return None
        else:
            return int(array[0])

    def set_timeout(self, val):
        self.timeout = val
        self.port.timeout = val  # Set read timeout
        self.port.write_timeout = val  # Set write timeout

    def enableErrorCallback(self, value):
        self.port.is_callback = value
    
    def getErrorCallback(self):
        return self.error_callback
        
    def setErrorCallback(self, callback):
        self.error_callback = callback
        self.port.setErrorCallback(callback)

    def setName(self, name):
        self.port.setName(name)


if __name__ == os.path.basename(os.path.dirname( __file__))+'.'+os.path.splitext(os.path.basename(__file__))[0]:
    SerialPort.getSerialPortMap()

############################################################################
if __name__ == "__main__":
    import os, sys

    device = "/dev/ttyAMA0"
    sp = SerialPort(device)
    sp.set_timeout(4)
    i = 0
    while True:
        try:
            v = sp.read("version")
            bv = v >> 16
            sv = v & 0xFFFF
            version = f"{bv}.{sv}"
            print(f"{i} MLCU version: {version}")

            sp.write("krt_led 181")


            i += 1
        except Exception as e:
            print(e)

    sp.close()
